#include <boost/bind.hpp>


#include "Internal/mpHttpGenericConnection.h"
#include "Internal/mpHttpSslConnection.h"

#include "mpHttpConnectionManage.h"

#include "mpHttpConfig.h"
#include "mpLog.h"

using namespace MP::HTTP::INTERNAL;

namespace MP{
namespace HTTP{

HttpConnectionManage::HttpConnectionManage(boost::asio::io_context& io_context)
    :_io_context(io_context),_connection_pool(),_in_use_connections(),_mutex_in_use_connections(),_mutex_connection_pool()
{
    //允许活动的HttpConnection最大数量
    _max_count_allocations = HttpConfig::GetInstance().get_pool_max_count_allocations();
    //poll中允许缓存的最大HTTP连接数量
    _max_count_inpool = HttpConfig::GetInstance().get_pool_max_count_inpool();
    //poll中每个KEY允许缓存的最大HTTP连接数量
    _max_count_preKey = HttpConfig::GetInstance().get_pool_max_count_preKey();

    //pool中Http连接缓存有效时间（秒）
    _max_cache_seconds = HttpConfig::GetInstance().get_pool_max_cache_seconds();
    //清理过期或者已关闭的Http连接的时间的最小间隔（秒）
    _min_interval_clean = HttpConfig::GetInstance().get_pool_min_interval_clean();
}


HttpConnectionManage::~HttpConnectionManage()
{
#if defined(MAPLE_MODE_DEBUGGING)
    MPLOG_INFO << "HttpConnectionManage is delete. http connection count in pool :" << _count_inpool  ;
#endif
}

HttpConnection::ptr_s HttpConnectionManage::getHttpConnection(const std::string& schema,
                                                              const std::string& host,
                                                              const std::string& port,
                                                              const SslSettings& sslSettings){

    if(_is_deactivate){
        return nullptr;
    }

    std::string key;
    calculationHttpConnectionKey(schema,host,port,key);

    HttpConnection::ptr_s httpConnection;
    //先尝试从缓存中取
    std::time_t now = std::time(NULL);
    {
        std::lock_guard<std::mutex> lock_(_mutex_connection_pool);

        //std::queue<HttpConnection::ptr_s>& queue = _connection_pool[key];

        auto iter = _connection_pool.find(key);
        if(iter !=_connection_pool.end()){
            //如果存在，则判断std::queue的元素个数，确保其个数不超过_max_count_preKey后再写入
            std::queue<HttpConnection::ptr_s>& queue = iter->second;
            while(!queue.empty() && !httpConnection)
            {
                httpConnection = queue.front();
                queue.pop();
                _count_inpool -=1;

                //检查连接是否已过有效期
                double duration = std::difftime(now , httpConnection->getLastUseTime());
                if(duration > _max_cache_seconds){
#if defined(MAPLE_MODE_DEBUGGING)
                    MPLOG_INFO << "(get)HttpConnection ( id = " << httpConnection->getId() << ") is overdue, it will pop next."  ;
#endif
                    httpConnection->disConnected();
                    httpConnection.reset();
                }
                else{
                    //检查连接是否已被关闭
                    if(!httpConnection->isConnected()){
#if defined(MAPLE_MODE_DEBUGGING)
                        MPLOG_INFO << "(get)HttpConnection ( id = " << httpConnection->getId() << ") is close, it will pop next."  ;
#endif
                        httpConnection.reset();
                    }
                }
            }
        }
    }

    //然后再...
    if(httpConnection){
        //已经从缓存中取到连接(此处可能会导致连接数量超过最大上限)
        std::lock_guard<std::mutex> lock_(_mutex_in_use_connections);

        std::weak_ptr<HttpConnection> weak_ptr(httpConnection);
        _in_use_connections[httpConnection->getId()] = weak_ptr;

#if defined(MAPLE_MODE_DEBUGGING)
            MPLOG_INFO << "HttpConnection ( id = " << httpConnection->getId() << ") get from cache."  ;
#endif
    }
    else{
        //缓存中未取到连接
        std::lock_guard<std::mutex> lock_(_mutex_in_use_connections);

        std::size_t in_use_count = _in_use_connections.size();
        if(_max_count_allocations > in_use_count){
            //能创建新的连接
            httpConnection = createNew(schema,host,sslSettings);
            httpConnection->setPoolKey(key);

#if defined(MAPLE_MODE_DEBUGGING)
            MPLOG_INFO << "HttpConnection ( id = " << httpConnection->getId() << ") is create."  ;
#endif
            std::weak_ptr<HttpConnection> weak_ptr(httpConnection);
            _in_use_connections[httpConnection->getId()] = weak_ptr;
        }
        else{
            MPLOG_ERROR << "cannot create connection - too many connections." ;
        }

    }
    return httpConnection;
}

//回收HttpConnection
void HttpConnectionManage::recoveryHttpConnection(HttpConnection::ptr_s httpConnection){

    if(_is_deactivate){
        return;
    }

    if(!httpConnection){
        return;
    }

    bool is_in_use = false;
    //先：从in_use集合中删除
    {
        std::lock_guard<std::mutex> lock_(_mutex_in_use_connections);

        if(_in_use_connections.erase(httpConnection->getId())){
#if defined(MAPLE_MODE_DEBUGGING)
            MPLOG_INFO << "HttpConnection ( id = " << httpConnection->getId() << ") will move to cache ."  ;
#endif
            is_in_use = true;
        }
    }

    //再：添加至缓存POOL集合中
    //如果不在in_use_connections中则认为该http连接并非由当前管理器创建，则不就行缓存
    if(!is_in_use)
    {
        //这种情况有两种可能（将取消缓存）：
        //第一：该connection并非由当前HttpConnectionManage创建
        //第二：该connection虽然由当前HttpConnectionManage创建，但是connection被重复执行了缓存操作）
        MPLOG_ERROR << "cancel cache connection,because httpConnection ( id = " << httpConnection->getId() << ") is not remove in used connections";
    }
    else
    {
        std::lock_guard<std::mutex> lock_(_mutex_connection_pool);

        auto iter= _connection_pool.find(httpConnection->getPoolKey());
        if(iter==_connection_pool.end()){

            //如果缓存数据已大于等于最大缓存数量，则先尝试进行清理
            if(_count_inpool >= _max_count_inpool){
                cleanHttpPoolConnections();
            }

            if(_count_inpool < _max_count_inpool){
                //如果没有找到，则新建一个
                httpConnection->setLastUseTime(std::time(NULL));

                std::queue<HttpConnection::ptr_s> queue;
                queue.push(httpConnection);
                _count_inpool+=1;

                _connection_pool[httpConnection->getPoolKey()] = queue;
            }
            else{
                MPLOG_ERROR << "cancel cache connection,because connection in pool count [ " << _count_inpool << " ] greater than max limit allowed [ " << _max_count_inpool << " ]" ;
            }
        }
        else{
            //如果存在，则判断std::queue的元素个数，确保其个数不超过_max_count_preKey后再写入
            std::queue<HttpConnection::ptr_s>& queue = iter->second;
            while(queue.size() >= _max_count_preKey){
                HttpConnection::ptr_s temp = queue.front();
                queue.pop();
                _count_inpool-=1;

                temp->disConnected();

#if defined(MAPLE_MODE_DEBUGGING)
                MPLOG_INFO << "remove old connection [ " << httpConnection->getId() << " ], because pre key connection cache count greater than max limit allowed [ " << _max_count_preKey << " ]" ;
#endif
            }

            //如果缓存数据已大于等于最大缓存数量，则先尝试进行清理
            if(_count_inpool >= _max_count_inpool){
                cleanHttpPoolConnections();
            }

            if(_count_inpool < _max_count_inpool){
                httpConnection->setLastUseTime(std::time(NULL));
                queue.push(httpConnection);
                _count_inpool+=1;
            }
            else{
                MPLOG_ERROR << "cancel cache connection [ " << httpConnection->getId() << " ],because connection in pool count [ " << _count_inpool << " ] greater than max limit allowed [ " << _max_count_inpool << " ]" ;
            }
        }
    }
}

//关闭所有正在执行的HttpConnection
void HttpConnectionManage::closeIsRunning(){

#if defined(MAPLE_MODE_DEBUGGING)
    MPLOG_INFO << "HttpConnectionManage begin close running connection ."  ;
#endif

    std::lock_guard<std::mutex> lock_(_mutex_in_use_connections);

    //关闭在用的连接
    std::unordered_map<std::string, std::weak_ptr<HttpConnection>>::iterator tmp_in_use;
    for (tmp_in_use = _in_use_connections.begin();tmp_in_use != _in_use_connections.end();tmp_in_use++)
    {
        std::weak_ptr<HttpConnection>& weak_ptr = tmp_in_use->second;
        //        if(!weak_ptr.expired()){ }
        if (auto ptr = weak_ptr.lock()) {
            ptr->disConnected();
        }
    }
    _in_use_connections.clear();

}

std::size_t HttpConnectionManage::getConnectionInPoolCount(){
    std::lock_guard<std::mutex> lock_(_mutex_connection_pool);
    return _count_inpool;
}

std::size_t HttpConnectionManage::getConnectionInUseCount(){
    std::lock_guard<std::mutex> lock_(_mutex_in_use_connections);
    return _in_use_connections.size();
}


//清空所有HttpConnection并停用
void HttpConnectionManage::clearAndDeactivate(){

    _is_deactivate = true;

#if defined(MAPLE_MODE_DEBUGGING)
    MPLOG_INFO << "HttpConnectionManage begin clear all connection ."  ;
#endif

    closeIsRunning();

    {

#if defined(MAPLE_MODE_DEBUGGING)
    MPLOG_INFO << "HttpConnectionManage begin clear cache connection ."  ;
#endif

        std::lock_guard<std::mutex> lock_(_mutex_connection_pool);

        //关闭缓存的连接
        std::unordered_map<std::string, std::queue<HttpConnection::ptr_s>>::iterator tmp_in_pool;
        for (tmp_in_pool = _connection_pool.begin();tmp_in_pool != _connection_pool.end();tmp_in_pool++)
        {
            std::queue<HttpConnection::ptr_s>& queue = tmp_in_pool->second;
            if(queue.empty()){
                continue;
            }

            while(!queue.empty()){
                HttpConnection::ptr_s httpConnection = queue.front();
                queue.pop();
                _count_inpool-=1;

                httpConnection->disConnected();
                //httpConnection.reset();
            }
        }
        _connection_pool.clear();
        //重置清理时间
        _last_clean_time = std::time(NULL);
    }

}


//创建一个新的HttpConnection
HttpConnection::ptr_s HttpConnectionManage::createNew(const std::string& schema,
                                                      const std::string& host,
                                                      const SslSettings& sslSettings){


    if(checkIsUseHttps(schema)){
        //HTTPS
        HttpConnection::ptr_s httpConnection(new HttpSslConnection(_io_context,host,sslSettings),
                                             boost::bind(&HttpConnectionManage::releaseHttpConnection, shared_from_this(), boost::placeholders::_1));
        return httpConnection;
    }
    else{
        //HTTP
        HttpConnection::ptr_s httpConnection(new HttpGenericConnection(_io_context),
                                             boost::bind(&HttpConnectionManage::releaseHttpConnection, shared_from_this(), boost::placeholders::_1));
        return httpConnection;
    }

}

//释放HttpConnection
void HttpConnectionManage::releaseHttpConnection(HttpConnection* httpConnection){
    if(httpConnection){
        if(!_is_deactivate)
        {
            std::lock_guard<std::mutex> lock_(_mutex_in_use_connections);

            if(_in_use_connections.erase(httpConnection->getId())){
#if defined(MAPLE_MODE_DEBUGGING)
                MPLOG_INFO << "HttpConnection ( id = " << httpConnection->getId() << ") is not in use , will release."  ;
#endif
            }
        }

#if defined(MAPLE_MODE_DEBUGGING)
        std::string c_id = httpConnection->getId();
#endif

        httpConnection->disConnected();

        delete httpConnection;

#if defined(MAPLE_MODE_DEBUGGING)
        MPLOG_INFO << "connection release complete . ( " << c_id << " )";
#endif

    }


}

//清理过期或者已关闭的Http连接（缓存的）(1分钟内最多执行一次)
void HttpConnectionManage::cleanHttpPoolConnections(){

    std::time_t now = std::time(NULL);

    double duration = std::difftime(now  , _last_clean_time);
    if(duration > _min_interval_clean)
    {
        std::unordered_map<std::string, std::queue<HttpConnection::ptr_s>>::iterator tmp;
        for (tmp = _connection_pool.begin();tmp != _connection_pool.end();tmp++)
        {
            std::queue<HttpConnection::ptr_s>& queue = tmp->second;
            if(queue.empty()){
                continue;
            }

            std::queue<HttpConnection::ptr_s> newQueue;
            while(!queue.empty()){
                HttpConnection::ptr_s httpConnection = queue.front();
                queue.pop();
                _count_inpool-=1;

                if(httpConnection->isConnected()){
                    //检查连接是否已过有效期
                    double duration2 = std::difftime(now , httpConnection->getLastUseTime());
                    if(duration2 > _max_cache_seconds){
                        httpConnection->disConnected();
    #if defined(MAPLE_MODE_DEBUGGING)
                        MPLOG_INFO << "(clean)HttpConnection ( id = " << httpConnection->getId() << ") is overdue or close ."  ;
    #endif
                    }
                    else{
                        newQueue.push(httpConnection);
                    }
                }

            }

            queue.swap(newQueue);
            _count_inpool += queue.size();
        }

        _last_clean_time = std::time(NULL);
    }
}

void  HttpConnectionManage::calculationHttpConnectionKey(const std::string& schema,
                                                         const std::string& host,
                                                         const std::string& port,
                                                         std::string & key){
    key.clear();

    key.append(schema);
    key.append("://");
    key.append(host);
    key.append(":");
    key.append(port);
}

HttpConnectionManage::ptr_s createHttpConnectionManage(boost::asio::io_context& io_context){
    return std::make_shared<HttpConnectionManage>(io_context);
}

}   //HTTP
}   //MP
